/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <front/mem.h>
#include <front/text.h>
#include <front/print_support.h>

#include "declarations.h"
#include "cfrontPrint.h"
#include "cfront_parse.h"
#include "fe_flags.h"
#include "init.h"
#include "spectype.h"
#include "type.h"

/* we need to use to original free */
#ifdef free
#undef free
#endif

#define Aggr_id(d) (declaration_specifier_tag(d)==Struct_kind?Struct_id(d):Union_id(d))

/************/


Scope Enum_Scope(declarator e)
{
  Scope sc = declarator_scope(e);
  
  if (declarator_tag(e) != enumerator_kind) return sc;
  for (; sc; sc = cfront_Scope_next(sc)) {
    if (cfront_Scope_tag(sc) == StructDefScope_kind) continue;
    if (cfront_Scope_tag(sc) == UnionDefScope_kind) continue;
    return sc;
  }
  return sc;
}

Scope Struct_Scope(declaration_specifier e)
{
  Scope sc = declaration_specifier_scope(e);
  
  for (; sc; sc = cfront_Scope_next(sc)) {
    if (cfront_Scope_tag(sc) == StructDefScope_kind) continue;
    if (cfront_Scope_tag(sc) == UnionDefScope_kind) continue;
    if (cfront_Scope_tag(sc) == StructDeclScope_kind) continue;
    if (cfront_Scope_tag(sc) == UnionDeclScope_kind) continue;
    return sc;
  }
  return sc;
}

Scope Statement_Scope (Scope s, Bool outer)
{
  Scope s1;
  statement stat;
  
  if (s==NULL) return s;
  s1 = Statement_Scope (cfront_Scope_next(s), outer);
  if (outer && s1 != NULL) return s1; /* Return outermost scope */
  if (cfront_Scope_tag(s) != compound_statementScope_kind) return s1;
  stat = compound_statementScope_data(s);
  if (stat!=NULL && statement_tag(stat) == compound_statement_kind) return s;
  return s1;
}

/************/

Ident
unique_ident(void)
{
  static int i=0;
  char *s;
  do {
    s = Front_StrAllocf("_uniq_%d",i);
    if (!Front_Find_identifier(s)) break;     
    fe_free (s);
    i++;
  } while (TRUE);
  return Create_Ident(s);
}

Ident 
get_parameter_id (declarator d)
{
  declarator d0 = get_Id_Decl(d);
  if (d0) d = d0;
  Ident id = NULL;
  
  switch (declarator_tag(d)) {
    case Id_Decl_kind: 
      id = Id_Decl_id(d); 
      break;
    case Par_Id_Decl_kind: 
      id = Par_Id_Decl_id(d); 
      break;
    default: 
    break;
  } 
  return id;
}

Bool
name_is_declared(Ident id, Scope scope, int loc)
{
  if (loc<0) loc = Scope_Num_Stores(scope)+1;
  return RetrieveBind(id, scope, loc, cfront_DefaultNameSpace,
                      Bind_cfront_declarator_kind)!=NULL;
}

void
append_specifier (declaration me, declaration_specifier ds)
{
  declaration_specifiers specs = declaration_specs (me);
  if (!ds) return;
  FRONT_APPEND_NODE (declaration_specifiers_specs (specs), ds);
}

void
prepend_specifier (declaration me, declaration_specifier ds)
{
  declaration_specifiers specs = declaration_specs (me);
  if (!ds) return;
  FRONT_PREPEND_NODE (ds, declaration_specifiers_specs (specs));
}

Bool
is_typedef (declarator e)
{
  return declarator_storage_class(e)==Typedef;
}

Bool
is_static (declarator e)
{
  return declarator_storage_class(e)==Static;
}

Bool
is_extern (declarator e)
{
  return declarator_storage_class(e)==Extern;
}

Bool
is_inline (declarator e)
{
  return declarator_is_inline (e);
}

Bool
has_storage_class (declarator me)
{ 
  return declarator_storage_class(me) != NoSCS;
}

Bool
is_global (declarator e)
{
  Scope s;
  declarator decl;
  
  if (e == NULL) return FALSE;
  decl = get_Id_Decl(e);
  if (decl) s = declarator_scope (decl); else s = declarator_scope (e);
  if (s) return Scope_tag (s) == cfrontScope_kind;
  return FALSE;
}

Bool file_scope (declarator e)
{
  Scope s = NULL;
  
  declarator decl = get_Id_Decl(e);
  if(decl) s = declarator_scope (decl);
  if (s==NULL) return FALSE;
  return cfront_Scope_tag(s) == cfrontScope_kind;
}

Bool
has_static_storage (declarator me)
{ /* 3.1.2.4 */
  return ( is_static (me) ||
           is_extern (me) ||
           (!is_typedef(me) && file_scope (me)) );
}

static Bool
Id_Decl_is_a_function (Id_Decl me)
{
  Type t = declarator_type (me);
  return is_function_type (t);
}

Bool
has_automatic_storage (declarator me)
{ /* 3.1.2.4 */
  return !has_static_storage(me);
}

Bool 
has_no_linkage (Id_Decl me)
{
  Bool is_object;
  
  assert (declarator_tag (me) == Id_Decl_kind);
  
  is_object = !Id_Decl_is_a_function (me) && !is_aggr_member(me) && 
              !is_typedef(me);
  
  /* 3.1.2.2.24 */
  if (!is_object && !Id_Decl_is_a_function (me)) return TRUE;
  
  /* 3.1.2.2.25 */
  if (is_object && !file_scope(me) && !is_extern(me)) return TRUE;
  
  return FALSE;
}

Bool
has_external_linkage (Id_Decl me)
{ /* 3.1.2.2 */
  Id_Decl first = Id_Decl_first (me);
  Bool is_object;
  
  assert (declarator_tag (me) == Id_Decl_kind);
  
  is_object = !Id_Decl_is_a_function (me) && !is_aggr_member(me) && 
              !is_typedef(me);
  
  /* 3.1.2.2.24, 3.1.2.2.25 */
  if (has_no_linkage(me)) return FALSE;
  
  /* 3.1.2.2.14 */
  if (is_static (me)) return FALSE;
  
  /* 3.1.2.2.16, 20 */
  if ((is_extern (me) || (Id_Decl_is_function (me) && !has_storage_class(me)))) {
    if (first && file_scope (first)) {
      /* extern scs, or function without scs has same linkage as file
	 scope identifier */
      return has_external_linkage (first);
    } else {
      return TRUE;
    }
  }

  /* 3.1.2.2.21 */
  if (is_object && file_scope(me) && !has_storage_class(me)) {
    return TRUE;
  }
  
  return FALSE;
}

Bool
has_internal_linkage (declarator me)
{ /* 3.1.2.2 */
  return !has_no_linkage(me) && !has_external_linkage(me);
}

Bool
function_has_result (FuncDecl me)
{
  Id_Decl i = get_Id_Decl (me);
  assert (declarator_tag (me) == FuncDecl_kind);
  
  return !is_void_type (function_result_type (declarator_type (i)));
}


Bool
is_decl_in_memory (Id_Decl d)
{
  Type t = declarator_type(d);
  if (is_typedef (d)) return FALSE;
  if (!is_object_type(t) && !is_incomplete_type(t)) return FALSE;
  return ( has_static_storage (d) ||
           is_structured_type (t) ||
           is_array_type (t) ||
           Id_Decl_address_taken(d) );
}

Bool
is_aggr_member (declarator e)
{ 
  Scope sc = declarator_scope(e);
  
  for (; sc; sc = cfront_Scope_next(sc)) {
    if (cfront_Scope_tag(sc) == StructDefScope_kind) return TRUE;
    if (cfront_Scope_tag(sc) == UnionDefScope_kind) return TRUE;
  }
  return FALSE;
}

Bool
is_parameter (declarator me)
{ 
  Scope s = declarator_scope(me);
  if (!s) return FALSE;
  return Scope_tag(s) == cfront_parameter_listScope_kind;
}

int
declarator_size (declarator d)
{
  Bool ok = TRUE;
  int size;
  assert (declarator_type (d));
  assert (!is_incomplete_type(declarator_type (d)));
  size = sizeof_type (declarator_type (d), &ok);
  assert (ok);
  return size;
}

int
declarator_align (declarator d)
{
  Bool ok = TRUE;
  int align;
  assert (declarator_type (d));
  align = alignof_type (declarator_type (d), &ok);
  assert (ok);
  return align;
}

/* ----------------------------------------------------------------------------
 * Get the Id_Decl of a declarator
 */
static declarator
Action_get_id_Id_Decl (declarator me, declarator id)
{
  if (id != NULL) return id;
  return me;
}

declarator
get_Id_Decl (declarator d)
{
  static struct s_cfront_TraverseTable tab;
  declarator p;

  tab.action_Id_Decl[POST_ACTION] = (void*)Action_get_id_Id_Decl;
  tab.action_Par_Id_Decl[POST_ACTION] = (void*)Action_get_id_Id_Decl;

  p = Traverse_declarator (d, &tab, NULL);
  return p;

}
/********************/



Ident get_externalDeclaration_id (externalDeclaration d)
{ function_header f;
  declarator e = NULL;
  
  if (d == NULL) return NULL;
  
  switch (externalDeclaration_tag(d)) {
    case FuncDef_kind:
      f = functionDefinition_head(FuncDef_func(d));
      if (f == NULL) break;
      e = function_header_decl (f);
      break;
    case PlainDef_kind:
      if (declaration_decls(PlainDef_decl (d)) == NULL) break;
      e = List_declarator_data (declaration_decls(PlainDef_decl (d)));
      break;
    case PragmaDef_kind:
    case NoDef_kind:
      break;
  }
  e = get_Id_Decl (e);
  if (e==NULL) return NULL;
  if (declarator_tag(e) != Id_Decl_kind) return NULL;
  return Id_Decl_id(e);
}

/********************/

static Ident temp_id (void)
{
  static Int num = 0;
  char s[1024];
  
  sprintf (s, "__temp_%d", num++);
  return Make_identifier (s);
}

expression
Make_temp_expression (Type t, expression init, declaration *p, cfront_Scope s)
{
  Ident temp = temp_id ();
  expression e = Parse_Type_expression(t, s, "%Identsym",1,temp);
  declaration d = initialize_variable (t, temp, init);
  declarator d1 = get_Id_Decl (List_declarator_data(declaration_decls (d)));
  expression_type (e) = t;
  declarator_type (d1) = t;
  IdExpr_decl(e) = d1;
  *p = d;
  return e;
}

/*********** Copies *********/

declaration
Deep_copy_decl (declaration d)
{
  declaration x = Traverse_copy_declaration (d, &cfront_copy_actions);
  ast_decorate (declaration, x, NULL, NULL, NULL);
#warning To do: get the function definition from decl
  return x;
}

List_declaration
Deep_copy_decls (List_declaration d)
{
  List_declaration x = Traverse_copy_List_declaration (d, &cfront_copy_actions);
  ast_decorate (List_declaration, x, NULL, NULL, NULL);
#warning To do: get the function definition from decl
  return x;
}

type_name
Deep_copy_type_name (type_name d)
{
  type_name x = COPY_type_name (d);
  ast_decorate (type_name, x, NULL, NULL, NULL);
  return x;
}


/********************/

declaration
add_declaration (statement s, declaration d)
{
  while (statement_tag (s) != compound_statement_kind) {
    s = statement_parent (s);
    assert (s);
  }
  FRONT_APPEND_NODE (compound_statement_locdecls (s), d);
  ast_cross_link (statement, s, statement_cur_func (s),
                  statement_parent (s), statement_scope (s));
  return d;
}

Id_Decl
add_temp_declaration (statement s, Type t)
{
  declaration d = declare_variable (t, unique_ident());
  add_declaration (s, d);
  return get_Id_Decl (HEAD(declaration_decls (d)));
}

void
function_prepend_parameter (FuncDecl func, declaration par)
{
  parameter_list p = FuncDecl_pars(func);

  if (parameter_list_pars(p) && !TAIL(parameter_list_pars(p))) {
    declaration q = HEAD (parameter_list_pars(p));
    declarator r;
    assert (!TAIL (declaration_decls (q)));
    r = get_Id_Decl (HEAD(declaration_decls (q)));
    Type t = int_type;
    if (r) t = expression_type (r); /* Can happen for old-style parameters with no declaration */
    if (is_void_type (t)) {
      parameter_list_pars(p) = NULL;
    }
  }
  parameter_list_pars(p) = Create_List_declaration(par, parameter_list_pars(p));
}

void
function_append_parameter (FuncDecl func, declaration par)
{
  parameter_list p = FuncDecl_pars(func);

  if (parameter_list_pars(p) && !TAIL(parameter_list_pars(p))) {
    declaration q = HEAD (parameter_list_pars(p));
    declarator r;
    assert (!TAIL (declaration_decls (q)));
    r = get_Id_Decl (HEAD(declaration_decls (q)));
    if (is_void_type (expression_type (r))) {
      parameter_list_pars(p) = NULL;
    }
  }
  FRONT_APPEND_NODE (parameter_list_pars(p), par);
}

/* ----------------------------------------------------------------------------
 * 
 *  Make declaration from a Type and an Identifier
 *
 * ----------------------------------------------------------------------------
 */

/* Use StructDef instead of StructDecl, and UnionDef instaed of
 * UnionDecl, in the declarations.
 */
static Bool expanded_structs = FALSE; 
static Bool expanded_typedefs = FALSE; 

void set_expand_structs (Bool expand)
{
  expanded_structs = expand;
}

void set_expand_typedefs (Bool expand)
{
  expanded_typedefs = expand;
}

/* ----------------------------------------------------------------------------
 * Translate a SpecType into type qualifiers
 */
static List_type_qualifier
make_qualifiers (SpecType t)
{
  List_type_qualifier result = NULL;
  
  if (!t) return NULL;
  assert (!SpecType_is_unsigned (t));
  assert (!SpecType_is_long (t));
  assert (!SpecType_is_bit_field (t));
  
  if (SpecType_is_restrict (t)) {
    result = Create_List_type_qualifier (Create_Restrict (no_srcinfo),
                                         result);
  }
  if (SpecType_is_const (t))  {
    result = Create_List_type_qualifier (Create_Const (no_srcinfo),
                                         result);
  }
  if (SpecType_is_volatile (t))  {
    result = Create_List_type_qualifier (Create_Volatile (no_srcinfo),
                                         result);
  }
  if (SpecType_attr (t))  {
    result = Create_List_type_qualifier (Create_GnuAttribute (no_srcinfo,
                                                              SpecType_attr (t)),
                                         result);
  }
  return result;
}

/* ----------------------------------------------------------------------------
 * Translate a SpecType into GNU attributes
 */
static gnu_attr
make_gnu_attr (SpecType t)
{
  if (!t) return NULL;
  assert (!SpecType_is_unsigned (t));
  assert (!SpecType_is_long (t));
  assert (!SpecType_is_bit_field (t));
  assert (!SpecType_is_restrict (t));
  assert (!SpecType_is_const (t));
  assert (!SpecType_is_volatile (t));
  
  if (SpecType_attr (t)) return SpecType_attr(t);
  return NULL;
}

/* ----------------------------------------------------------------------------
 * Create parameter declarations for FunctionType F
 */
static parameter_list
make_parameters (FunctionType  f)
{
  List_declaration decls = NULL;
  List_Type ps;
  
  for (ps = FunctionType_arg_types (f); ps; ps = TAIL(ps)) {
    Type p = HEAD(ps);
    FRONT_APPEND_NODE (decls, declare_variable (p, NULL));
  }
  return Create_parameter_list (no_srcinfo, decls);
}

/* ----------------------------------------------------------------------------
 * Create member declarations for StructType or UnionType T
 */
static List_declaration
make_members (AggrType t)
{
  List_declaration ds = NULL;
  List_SimpleDecl sds;
  
  for (sds = AggrType_mems (t); sds; sds = TAIL (sds)) {
    declaration decl;
    SimpleDecl sd = HEAD (sds);
    declarator d;
    NumExpr bitwidth = NULL;
    Type p = SimpleDecl_type (sd);
    
    if (is_bitfield_type (p)) {
      char *n = Front_StrAllocf ("%d", SpecType_bit_width(p));
      bitwidth = Create_NumExpr (no_srcinfo, n); 
    }
    if (!SimpleDecl_id (sd)) {
      assert (bitwidth);
      d = Create_NoDeclStruct (no_srcinfo, bitwidth);
      make_declaration (NULL, d);
    } else {
      d = Create_Id_Decl (no_srcinfo, SimpleDecl_id (sd));
      decl = make_declaration (p, d);
      d = HEAD (declaration_decls (decl));
      d = Create_DeclStruct (no_srcinfo, NULL, d, bitwidth);
      HEAD (declaration_decls (decl)) = d;
    }
    FRONT_APPEND_NODE (ds, decl);
  }
  return ds;
}

/* ----------------------------------------------------------------------------
 * Create enum members for EnumType T
 */
static List_declarator
make_enums (EnumType t)
{
  return EnumType_members (t);
}

/* ----------------------------------------------------------------------------
 * Add declaration specifier to ME from SpecType T
 */
static void
SpecType_add_specifiers(SpecType t, declaration me)
{
  int l;
  declaration_specifier ds;
  
  if (!t) return;
  if (SpecType_is_unsigned (t)) {
    ds = Create_Sign (no_srcinfo, UnSigned);
    append_specifier (me, ds);
  } else if (SpecType_is_signed (t)) {
    ds = Create_Sign (no_srcinfo, Signed);
    append_specifier (me, ds);
  }
  for (l = SpecType_is_long (t); l; l--) {
    ds = Create_TypeId (no_srcinfo, Create_Ident ("long"));
    append_specifier (me, ds);
  }
  if (SpecType_is_restrict (t)) {
    ds = Create_TypeQual (no_srcinfo, Create_Restrict (no_srcinfo));
    append_specifier (me, ds);
  }
  if (SpecType_is_const (t)) {
    ds = Create_TypeQual (no_srcinfo, Create_Const (no_srcinfo));
    append_specifier (me, ds);
  }
  if (SpecType_is_volatile (t)) {
    ds = Create_TypeQual (no_srcinfo, Create_Volatile (no_srcinfo));
    append_specifier (me, ds);
  }
  if (SpecType_attr (t)) {
    ds = Create_TypeQual (no_srcinfo, Create_GnuAttribute (no_srcinfo,
                                                           SpecType_attr (t)));
    append_specifier (me, ds);
  }
}

/* ----------------------------------------------------------------------------
 * Add declaration specifier to ME from SpecType T
 */
static declaration
add_specifiers (Type t, declaration me, Type spec)
{
  declaration_specifier ds = NULL;
  
  SpecType_add_specifiers (spec, me);

  switch (Type_tag (t)) {
  case IdType_kind:
    ds = Create_TypeId (no_srcinfo, IdType_id (t));
    break;
  case StructType_kind:
    if (expanded_structs && !AST_node_mark(t)) {
      AST_node_mark(t) = TRUE;
      ds = Create_StructDef (no_srcinfo, AggrType_atag(t), make_members (t));
      AST_node_mark(t) = FALSE;
    } else {
      ds = Create_StructDecl (no_srcinfo, AggrType_atag(t));
    }
    break;
  case UnionType_kind:
    if (expanded_structs && !AST_node_mark(t)) {
      AST_node_mark(t) = TRUE;
      ds = Create_UnionDef (no_srcinfo, AggrType_atag(t), make_members (t));
      AST_node_mark(t) = FALSE;
    } else {
      ds = Create_UnionDecl (no_srcinfo, AggrType_atag(t));
    }
    break;
  case EnumType_kind:
    ds = Create_Enum (no_srcinfo, EnumType_etag(t), make_enums (t));
    break;
  case SpecType_kind:
    return add_specifiers (SpecType_base (t), me, t);
    
  case EllipsisType_kind:
    break;
    
  case PointerType_kind:
  case ArrayType_kind:
  case FunctionType_kind:
    abort();
  }  
  append_specifier (me, ds);
  return me;
}

/* ----------------------------------------------------------------------------
 * Make a declaration from T and ME, T has SpecType SPEC
 */
static declaration
_make_declaration (Type t, declarator me, Type spec)
{
  declaration decl;

  if (t) {
    if (expanded_typedefs) t = expanded_type (t);
    switch (Type_tag (t)) {
    case SpecType_kind:
      return _make_declaration (SpecType_base (t), me, both_specs (t, spec));
    
    case PointerType_kind:
      me = Create_pointer_declarator (no_srcinfo, make_qualifiers (spec), me);
      me = Create_ParenDecl (no_srcinfo, me);
      return _make_declaration (PointerType_target (t), me, NULL);
    case ArrayType_kind:
      assert (!spec);
      me = Create_ArrayDecl (no_srcinfo, me, ArrayType_a_size (t));
      return _make_declaration (ArrayType_element (t), me, NULL);
    case FunctionType_kind:
      me = Create_FuncDecl (no_srcinfo, me, make_parameters (t),
                            make_gnu_attr (spec));
      return _make_declaration (FunctionType_result (t), me, NULL);
    
    case IdType_kind:
    case StructType_kind:
    case UnionType_kind:
    case EnumType_kind:
      break;
    case EllipsisType_kind:
      assert (!spec);
      me = Create_ellipsis_declarator (no_srcinfo);
    }
  }
  decl = Create_declaration (no_srcinfo, NULL/*scope*/,
                             Create_declaration_specifiers (no_srcinfo, NULL),
                             Create_List_declarator (me, NULL));
  return add_specifiers (t, decl, spec);
}

/* ----------------------------------------------------------------------------
 * Make a declaration from T an D
 */
declaration
make_declaration (Type t, declarator d)
{
  declaration r = _make_declaration (t, d, NULL);
  return r;
}

/* ----------------------------------------------------------------------------
 * Make a declaration "T ID"
 */
declaration
declare_variable (Type t, Ident id)
{
  return make_declaration (t, Create_Id_Decl (no_srcinfo, id));
}

/* ----------------------------------------------------------------------------
 * Make a declaration "T ID=INIT"
 */
declaration
initialize_variable (Type t, Ident id, expression init)
{
  declaration r = declare_variable (t, id);
  declarator d;
  
  if (!init) return r;
  d = HEAD(declaration_decls(r));
  HEAD(declaration_decls(r)) = Create_initDeclarator (no_srcinfo, d, init, NULL);
  return r;
}

/* ----------------------------------------------------------------------------
 * Make a type name "T ID"
 */
type_name
make_type_name (Type t)
{
  declaration d = declare_variable (t, NULL);
  return Create_type_name (declaration_src_info(d),
                           declaration_specs (d),
                           HEAD(declaration_decls (d)));
}
